﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections;
using BEG.Model;
using BEG.Model.Classifiers;
using BEG.Model.Projections;
using BEG.Core;
using BEG.Core.Interface;
using GalaSoft.MvvmLight.Command;
using System.Windows.Input;
using System.Xml;
using System.IO;
using BEG.Model.Attribute;

namespace WebReportGeneratorVer2
{
    /// <summary>
    /// Взаимодействие UI и генератора
    /// </summary>
    public partial class GeneratorForm : Form
    {
        /// <summary>
        /// Текущий BEG-проект с классификаторами и проекциями
        /// </summary>
        private Project project;

        public GeneratorForm(Project project)
        {
            this.project = project;
            InitializeComponent();
            addClassifiers();
        }

        /// <summary>
        /// Добавить проекции на форму
        /// </summary>
        /// <param name="projections">Список проекций</param>
        private void addProjections(ICollection<Projection> projections)
        {
            this.comboBox1.Items.Clear();
            foreach (Projection projection in projections)
            {
                this.comboBox1.Items.Add(projection.Name);
            }
            comboBox1.Refresh();
        }

        /// <summary>
        /// Добавить все классификаторы проекта на форму
        /// </summary>
        private void addClassifiers()
        {
            foreach (Classifier classifier in project.Classifiers)
            {
                this.checkedListBox1.Items.Add(classifier.Name);
            }
        }

        /// <summary>
        /// Обработчик, изменяющий список доступных проекций
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            List<Projection> availableProjections = getAvailableProjections();
            addProjections(availableProjections);

            List<Classifier> availableClassifiers = getAvailableClassifiers();
        }

        /// <summary>
        /// Отмена сохранения
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            this.Dispose();
        }

        /// <summary>
        /// Генерация отчетов по выбранным классификаторам
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            SaveFileDialog svd = new SaveFileDialog();
            svd.Filter = "HTML Files (*.html)|*.html|All Files (*.*)|*.*";

            Stream outputStream;
            StreamWriter writer;

            HtmlReportGenerator generator = new HtmlReportGenerator();
            Projection selectedProjection;
            //получаем выбранную проекцию
            if (this.comboBox1.SelectedItem != null)
            {
                selectedProjection = getProjectionByName(this.comboBox1.SelectedItem.ToString());
            }
            else
            {
                selectedProjection = new Projection();
                project.Projections.Add(selectedProjection);
            }
            
            //получаем список выбранных классификаторов
            List<Classifier> selectedClassifiers = new List<Classifier>();
            Classifier first = null;
            Classifier second = null;
            foreach (String item in checkedListBox1.CheckedItems)   //SelectedItems
            {
                Classifier c = getClassifierByName(item);
                selectedClassifiers.Add(c);
                addFictiveClassifier(selectedProjection, selectedClassifiers, c);

                if (first == null)
                {
                    first = getClassifierByName(item);
                    continue;
                }
                if (first != null && second == null)
                {
                    second = getClassifierByName(item);
                }
            }

            //сохранение
            if (svd.ShowDialog() == DialogResult.OK)
            {
                if ((outputStream = svd.OpenFile()) != null)
                {
                    writer = new StreamWriter(outputStream, Encoding.UTF8);

                    if (!checkBox1.Checked)
                    {
                        /*
                         * Если в основной файл все сохраняется без атрибутов, 
                         * то рядом будет лежать директория с отдельными классификаторами и их атрибутами
                         */
                        generator.writeClassifiersOneByOne(selectedClassifiers, svd.FileName, selectedProjection);
                    }

                    if (selectedClassifiers.Count == 1)
                    {
                        generator.writeClassifier(getClassifierByName(checkedListBox1.CheckedItems[0].ToString()), writer, checkBox1.Checked);
                    }

                    if (selectedClassifiers.Count > 1)
                    {

                        //записываем все классификаторы в файл
                        generator.writeClassifiers(selectedClassifiers, writer, selectedProjection, checkBox1.Checked);
                    }

                    writer.Close();
                    outputStream.Close();
                }
            }

            this.Close();
        }

        /// <summary>
        /// Вставляет в список классификаторов для сохранения фиктивный классификатор, представляющий
        /// вложенный уровень элементов классификатора
        /// </summary>
        /// <param name="selectedProjection">Выбранная проекция</param>
        /// <param name="selectedClassifiers">Выбранные классификаторы</param>
        /// <param name="c">Рассматриваемый классификатор</param>
        private void addFictiveClassifier(Projection selectedProjection, List<Classifier> selectedClassifiers, Classifier c)
        {
            if (c.Items[0].Items[0].Items != null && c.Items[0].Items[0].Items.Count > 0)
            {

                // Создание классификатора
                var fictiveClassifier = new Classifier { Name = "Фиктивный классификатор" };
                // Создания двух позиций классификатора
                var titleItem = new ClassifierItem { Name = "Название" };

                project.AddClassifier(fictiveClassifier);
                fictiveClassifier.Add(titleItem);
                selectedClassifiers.Add(fictiveClassifier);

                foreach (AttributeHeader header in c.Attributes)
                {
                    fictiveClassifier.AddAttribute(header);
                }
                fictiveClassifier.RemoveAttribute(fictiveClassifier.Attributes.ElementAt(0));
                fictiveClassifier.RemoveAttribute(fictiveClassifier.Attributes.ElementAt(1));

                //расставляем сслыки между разными уровнями
                foreach (ClassifierItem child in c.Items[0].Items)
                {
                    foreach (ClassifierItem childOfTheChild in child.Items)
                    {
                        List<ClassifierAttribute> attributes = new List<ClassifierAttribute>();
                        foreach (ClassifierAttribute attr in childOfTheChild.Attributes)
                        {
                            AttributeHeader header = attr.HeaderObject;
                            attributes.Add(attr);
                        }

                        titleItem.Add(childOfTheChild);

                        if (childOfTheChild.Attributes != null)
                        {
                            for (int i = 0; i < attributes.Count; i++)
                            {
                                childOfTheChild.Attributes.RemoveAt(0);
                                childOfTheChild.Attributes.Add(attributes[i]);
                            }
                        }

                        //создаем ссылку между k и (k+1) уровнем классификатора
                        var references = new Dictionary<ReferenceDescription, ClassifierItem>
                            {
                                {selectedProjection.LeftRef, child},
                                {selectedProjection.RightRef, childOfTheChild}
                             };
                        var newLink = selectedProjection.CreateLink(references);

                    }
                }
                //добавляем следующий уровень
                addFictiveClassifier(selectedProjection, selectedClassifiers, fictiveClassifier);
            }
        }

        /// <summary>
        /// Получает классификатор по его имени
        /// </summary>
        /// <param name="name">Имя классификатора</param>
        /// <returns>Классификатор из данного проекта с требуемым именем</returns>
        private Classifier getClassifierByName(String name)
        {
            foreach (Classifier c in project.Classifiers)
            {
                if (c.Name.Equals(name))
                {
                    return c;
                }
            }
            return null;
        }

        /// <summary>
        /// Получает проекцию по её имени
        /// </summary>
        /// <param name="name">Имя проекции</param>
        /// <returns>Проекция из данного проекта с требуемым именем</returns>
        private Projection getProjectionByName(String name)
        {
            foreach (Projection p in project.Projections)
            {
                if (p.Name.Equals(name))
                {
                    return p;
                }
            }
            return null;
        }

        
        /// <summary>
        /// /выбирает проекции, соединенные с уже выбранными классификаторам
        /// </summary>
        /// <returns>Список доступных проекций</returns>
        private List<Projection> getAvailableProjections()
        {
            List<Projection> availableProjections = null;
            foreach (object item in checkedListBox1.CheckedItems)   //SelectedItems
            {
                string tmp = Convert.ToString(item);
                Classifier selected = getClassifierByName(tmp);
                if (availableProjections != null)
                {
                    availableProjections = availableProjections.Intersect(selected.GetProjections()).ToList();
                }
                else
                {
                    availableProjections = selected.GetProjections().ToList();
                }
            }
            return availableProjections ?? new List<Projection>();
        }

        /// <summary>
        /// выбирает классификаторы, соединенные с уже выбранными
        /// </summary>
        /// <returns>Список доступных классификаторов</returns>
        private List<Classifier> getAvailableClassifiers()
        {
            List<Classifier> availableClassifiers = new List<Classifier>();
            foreach (String item in checkedListBox1.CheckedItems)   //SelectedItems
            {
                Classifier selected = getClassifierByName(item);
                foreach (Projection p in selected.GetProjections())
                {
                    availableClassifiers.Add(ClassifierUtils.getConnected(selected, p));
                }
            }
            return availableClassifiers;
        }
    }
}
